namespace MefContrib.Models.Provider
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel.Composition;
    using System.ComponentModel.Composition.Hosting;
    using System.Reflection;
    using MefContrib.Models.Provider.Validation;

    public static class CompositionServices
    {
        public static string GetContractNameFromExportDescription(MemberInfo member, ExportDescription exportDescription)
        {
            member
                .Require("member")
                .NotNull();

            exportDescription
                .Require("exportDescription")
                .NotNull();

            if (!string.IsNullOrEmpty(exportDescription.ContractName))
            {
                return exportDescription.ContractName;
            }

            if (exportDescription.ContractType != null)
            {
                return AttributedModelServices.GetContractName(exportDescription.ContractType);
            }

            if (member.MemberType == MemberTypes.Method)
            {
                return AttributedModelServices.GetTypeIdentity((MethodInfo)member);
            }

            return AttributedModelServices.GetContractName(member.GetDefaultTypeFromMember());
        }

        public static string GetContractNameFromImportDescription(MemberInfo member, ImportDescription importDescription)
        {
            member
                .Require("member")
                .NotNull();

            importDescription
                .Require("importDescription")
                .NotNull();

            if (!string.IsNullOrEmpty(importDescription.ContractName))
            {
                return importDescription.ContractName;
            }

            if (importDescription.ContractType != null)
            {
                return AttributedModelServices.GetContractName(importDescription.ContractType);
            }

            if (member.MemberType == MemberTypes.Method)
            {
                return AttributedModelServices.GetTypeIdentity((MethodInfo)member);
            }

            return AttributedModelServices.GetContractName(member.GetDefaultTypeFromMember());
        }

        public static IDictionary<string, object> GetMetadataFromExportDescription(MemberInfo member, ExportDescription exportDescription)
        {
            member
                .Require("member")
                .NotNull();

            exportDescription
                .Require("exportDescription")
                .NotNull();

            var metadata = new Dictionary<string, object>();
            var contract = GetTypeIdentityFromExportDescription(member, exportDescription);

            metadata.Add(
                CompositionConstants.ExportTypeIdentityMetadataName, contract);

            if (exportDescription.Metadata != null)
            {
                foreach (var pair in exportDescription.Metadata)
                {
                    metadata.Add(pair.Key, pair.Value);
                }
            }

            return metadata;
        }

        public static string GetTypeIdentityFromExportDescription(this MemberInfo member, ExportDescription export)
        {
            if (export.ContractType != null)
            {
                return AttributedModelServices.GetTypeIdentity(export.ContractType);
            }

            if (member.MemberType == MemberTypes.Method)
            {
                return AttributedModelServices.GetTypeIdentity((MethodInfo)member);
            }

            if (member.MemberType == MemberTypes.Method)
            {
                return AttributedModelServices.GetTypeIdentity((MethodInfo)member);
            }

            return AttributedModelServices.GetTypeIdentity(member.GetDefaultTypeFromMember());
        }

        private static Type GetDefaultTypeFromMember(this MemberInfo member)
        {
            switch (member.MemberType)
            {
                case MemberTypes.Property:
                    return ((PropertyInfo)member).PropertyType;

                case MemberTypes.NestedType:
                case MemberTypes.TypeInfo:
                    return ((Type)member);

                case MemberTypes.Field:
                default:
                    return ((FieldInfo)member).FieldType;
            }
        }
    }
}